#include <GLFW/glfw3.h>
#include <OSGMain.hpp>
#include <iostream>

static OSGMain osg;

void error_callback(int error, const char* description)
{
    std::cerr << "GLFW_ERROR: '" << error << "'', description: '" << description << "'" << std::endl;
}

void processButtonEvent(int button, int action, int mods)
{
    Button::Type type;
    Button::Action actionType;
    Button::Modifiers modifiers{false, false, false, false};

    switch(action)
    {
        case GLFW_PRESS: actionType = Button::Action::Press; break;
        case GLFW_RELEASE: actionType = Button::Action::Release; break;
        case GLFW_REPEAT: actionType = Button::Action::Repeat; break;
        default: return;
    }

    if(mods & GLFW_MOD_SHIFT)
    {
        modifiers.Shift = true;
    }
    if(mods & GLFW_MOD_ALT)
    {
        modifiers.Alt = true;
    }
    if(mods & GLFW_MOD_CONTROL)
    {
        modifiers.Control = true;
    }
    if(mods & GLFW_MOD_SUPER)
    {
        modifiers.Super = true;
    }

    switch(button)
    {
        case GLFW_MOUSE_BUTTON_LEFT: type = Button::Type::MouseLeft; break;
        case GLFW_MOUSE_BUTTON_RIGHT:type = Button::Type::MouseRight; break;
        case GLFW_MOUSE_BUTTON_MIDDLE: type = Button::Type::MouseMiddle; break;
        case GLFW_KEY_SPACE: type = Button::Type::KeySpace; break;
        case GLFW_KEY_APOSTROPHE: type = Button::Type::KeyApostrophe; break;
        case GLFW_KEY_COMMA: type = Button::Type::KeyComma; break;
        case GLFW_KEY_MINUS: type = Button::Type::KeyMinus; break;
        case GLFW_KEY_PERIOD: type = Button::Type::KeyPeriod; break;
        case GLFW_KEY_SLASH: type = Button::Type::KeySlash; break;
        case GLFW_KEY_0: type = Button::Type::Key0; break;
        case GLFW_KEY_1: type = Button::Type::Key1; break;
        case GLFW_KEY_2: type = Button::Type::Key2; break;
        case GLFW_KEY_3: type = Button::Type::Key3; break;
        case GLFW_KEY_4: type = Button::Type::Key4; break;
        case GLFW_KEY_5: type = Button::Type::Key5; break;
        case GLFW_KEY_6: type = Button::Type::Key6; break;
        case GLFW_KEY_7: type = Button::Type::Key7; break;
        case GLFW_KEY_8: type = Button::Type::Key8; break;
        case GLFW_KEY_9: type = Button::Type::Key9; break;
        case GLFW_KEY_SEMICOLON: type = Button::Type::KeySemicolon; break;
        case GLFW_KEY_EQUAL: type = Button::Type::KeyEqual; break;
        case GLFW_KEY_A: type = Button::Type::KeyA; break;
        case GLFW_KEY_B: type = Button::Type::KeyB; break;
        case GLFW_KEY_C: type = Button::Type::KeyC; break;
        case GLFW_KEY_D: type = Button::Type::KeyD; break;
        case GLFW_KEY_E: type = Button::Type::KeyE; break;
        case GLFW_KEY_F: type = Button::Type::KeyF; break;
        case GLFW_KEY_G: type = Button::Type::KeyG; break;
        case GLFW_KEY_H: type = Button::Type::KeyH; break;
        case GLFW_KEY_I: type = Button::Type::KeyI; break;
        case GLFW_KEY_J: type = Button::Type::KeyJ; break;
        case GLFW_KEY_K: type = Button::Type::KeyK; break;
        case GLFW_KEY_L: type = Button::Type::KeyL; break;
        case GLFW_KEY_M: type = Button::Type::KeyM; break;
        case GLFW_KEY_N: type = Button::Type::KeyN; break;
        case GLFW_KEY_O: type = Button::Type::KeyO; break;
        case GLFW_KEY_P: type = Button::Type::KeyP; break;
        case GLFW_KEY_Q: type = Button::Type::KeyQ; break;
        case GLFW_KEY_R: type = Button::Type::KeyR; break;
        case GLFW_KEY_S: type = Button::Type::KeyS; break;
        case GLFW_KEY_T: type = Button::Type::KeyT; break;
        case GLFW_KEY_U: type = Button::Type::KeyU; break;
        case GLFW_KEY_V: type = Button::Type::KeyV; break;
        case GLFW_KEY_W: type = Button::Type::KeyW; break;
        case GLFW_KEY_X: type = Button::Type::KeyX; break;
        case GLFW_KEY_Y: type = Button::Type::KeyY; break;
        case GLFW_KEY_Z: type = Button::Type::KeyZ; break;
        case GLFW_KEY_LEFT_BRACKET: type = Button::Type::KeyLeftBracket; break;
        case GLFW_KEY_BACKSLASH: type = Button::Type::KeyBackslash; break;
        case GLFW_KEY_RIGHT_BRACKET: type = Button::Type::KeyRightBracket; break;
        case GLFW_KEY_GRAVE_ACCENT: type = Button::Type::KeyGraveAccent; break;
        case GLFW_KEY_WORLD_1: type = Button::Type::KeyWord1; break;
        case GLFW_KEY_WORLD_2: type = Button::Type::KeyWord2; break;
        case GLFW_KEY_ESCAPE: type = Button::Type::KeyEscape; break;
        case GLFW_KEY_ENTER: type = Button::Type::KeyEnter; break;
        case GLFW_KEY_TAB: type = Button::Type::KeyTab; break;
        case GLFW_KEY_BACKSPACE: type = Button::Type::KeyBackspace; break;
        case GLFW_KEY_INSERT: type = Button::Type::KeyInsert; break;
        case GLFW_KEY_DELETE: type = Button::Type::KeyDelete; break;
        case GLFW_KEY_RIGHT: type = Button::Type::KeyRight; break;
        case GLFW_KEY_LEFT: type = Button::Type::KeyLeft; break;
        case GLFW_KEY_DOWN: type = Button::Type::KeyDown; break;
        case GLFW_KEY_UP: type = Button::Type::KeyUp; break;
        case GLFW_KEY_PAGE_UP: type = Button::Type::KeyPageUp; break;
        case GLFW_KEY_PAGE_DOWN: type = Button::Type::KeyPageDown; break;
        case GLFW_KEY_HOME: type = Button::Type::KeyHome; break;
        case GLFW_KEY_END: type = Button::Type::KeyEnd; break;
        case GLFW_KEY_CAPS_LOCK: type = Button::Type::KeyCapsLock; break;
        case GLFW_KEY_SCROLL_LOCK: type = Button::Type::KeyScrollLock; break;
        case GLFW_KEY_NUM_LOCK: type = Button::Type::KeyNumLock; break;
        case GLFW_KEY_PRINT_SCREEN: type = Button::Type::KeyPrintScreen; break;
        case GLFW_KEY_PAUSE: type = Button::Type::KeyPause; break;
        case GLFW_KEY_F1: type = Button::Type::KeyF1; break;
        case GLFW_KEY_F2: type = Button::Type::KeyF2; break;
        case GLFW_KEY_F3: type = Button::Type::KeyF3; break;
        case GLFW_KEY_F4: type = Button::Type::KeyF4; break;
        case GLFW_KEY_F5: type = Button::Type::KeyF5; break;
        case GLFW_KEY_F6: type = Button::Type::KeyF6; break;
        case GLFW_KEY_F7: type = Button::Type::KeyF7; break;
        case GLFW_KEY_F8: type = Button::Type::KeyF8; break;
        case GLFW_KEY_F9: type = Button::Type::KeyF9; break;
        case GLFW_KEY_F10: type = Button::Type::KeyF10; break;
        case GLFW_KEY_F11: type = Button::Type::KeyF11; break;
        case GLFW_KEY_F12: type = Button::Type::KeyF12; break;
        case GLFW_KEY_LEFT_SHIFT: type = Button::Type::KeyLeftShift; break;
        case GLFW_KEY_LEFT_CONTROL: type = Button::Type::KeyLeftControl; break;
        case GLFW_KEY_LEFT_ALT: type = Button::Type::KeyLeftAlt; break;
        case GLFW_KEY_LEFT_SUPER: type = Button::Type::KeyLeftSuper; break;
        case GLFW_KEY_RIGHT_SHIFT: type = Button::Type::KeyRightShift; break;
        case GLFW_KEY_RIGHT_CONTROL: type = Button::Type::KeyRightControl; break;
        case GLFW_KEY_RIGHT_ALT: type = Button::Type::KeyRightAlt; break;
        case GLFW_KEY_RIGHT_SUPER: type = Button::Type::KeyRightSuper; break;
        case GLFW_KEY_MENU: type = Button::Type::KeyMenu; break;
        default: return;
    }

    osg.handleButtonEvent(type, actionType, modifiers);
}
void key_callback(GLFWwindow* /*window*/, int key, int /*scancode*/, int action, int mods)
{
    processButtonEvent(key, action, mods);
}

void button_callback(GLFWwindow* /*window*/, int button, int action, int mods)
{
    processButtonEvent(button, action, mods);
}

void position_callback(GLFWwindow* /*window*/, double xpos, double ypos)
{
    xpos = xpos >= 0 ? xpos : 0;
    ypos = ypos >= 0 ? ypos : 0;
    osg.setMousePosition(xpos, ypos);
}

void resize_callback(GLFWwindow* /*window*/, int width, int height)
{
    osg.setViewPort(width, height);
}

void refresh_callback(GLFWwindow* window)
{
    osg.redraw();
    glfwSwapBuffers(window);
}

void initializeViewPort(GLFWwindow* window)
{
    i32 width, height;
    glfwGetFramebufferSize(window, &width, &height);
    osg.setViewPort(width, height);
}

int main(int /*argc*/, char ** /*argv*/)
{
    glfwSetErrorCallback(error_callback);

    if(!glfwInit())
    {
        return -1;
    }
    else
    {
        GLFWwindow* window = glfwCreateWindow(800, 600, "Old School Game", NULL, NULL);

        if(window)
        {
            initializeViewPort(window);
            glfwSetKeyCallback(window, key_callback);
            glfwSetCursorPosCallback(window, position_callback);
            glfwSetMouseButtonCallback(window, button_callback);
            glfwSetFramebufferSizeCallback(window, resize_callback);
            glfwSetWindowRefreshCallback(window, refresh_callback);


            glfwMakeContextCurrent(window);
            glfwSwapInterval(1);


            while(!glfwWindowShouldClose(window))
            {
                osg.redraw(glfwGetTime());

                glfwSwapBuffers(window);
                glfwPollEvents();
            }
            glfwDestroyWindow(window);
            glfwTerminate();
            return 0;
        }
        else
        {
            return -2;
        }
    }
}
